/* 
Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/

This file is part of SimpleFTP.

This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/

$Author: pjm2 $
$Id: SimpleFTP.java,v 1.2 2004/05/29 19:27:37 pjm2 Exp $

*/

package simpleftp.client.control;

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * SimpleFTP is a simple package that implements a Java FTP client.
 * With SimpleFTP, you can connect to an FTP server and upload multiple files.
 *  <p>
 * Copyright Paul Mutton,
 *           <a href="http://www.jibble.org/">http://www.jibble.org/</a>
 * 
 */
public class SimpleFTP {

	/**
     * Create an instance of SimpleFTP.
     */
    public SimpleFTP() {
      
    }
    
    
    /**
     * Connects to the default port of an FTP server and logs in as
     * anonymous/anonymous.
     */
    public void connect(String host) throws IOException 
    {
        connect (host, 21);
    }
    
    /**
     * Connects to an FTP server and logs in as anonymous/anonymous.
     */
    public void connect(String host, int port) throws IOException 
    {
        connect(host, port, "anonymous", "anonymous");
    }
    
    
    /**
     * Connects to an FTP server and logs in with the supplied username
     * and password.
     */
    public void connect(String host, int port, String user, String pass) throws IOException 
    {
        if (socket != null) 
        {
            throw new IOException("SimpleFTP is already connected. Disconnect first.");
        }       
        socket = new Socket(host, port);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
       // TO BE COMPLETED - 1 LINE
        /* Change : We use this object to receive a File from the server*/
        entree = new ObjectInputStream(socket.getInputStream());
        
        String response = readLine();
        if (!response.startsWith("220 ")) 
        {
            throw new IOException("SimpleFTP received an unknown response when connecting to the FTP server: " + response);
        }
        
        sendLine("USER " + user);        
        response = readLine();
        
        if (!response.startsWith("331 ")) {
        	System.out.println("2");
            throw new IOException("SimpleFTP received an unknown response after sending the user: " + response);
        }
        sendLine("PASS " + pass);
        
        response = readLine();
        if (!response.startsWith("230 ")) {
            throw new IOException("SimpleFTP was unable to log in with the supplied password: " + response);
        }
        // Now logged in.
    }    
    
    /**
     * Disconnects from the FTP server.
     */
    public  void disconnect() throws IOException 
    {
        try 
        {
            sendLine("QUIT");
        }
        finally {
            socket = null;
        }
    }
    
    
    /**
     * Returns the working directory of the FTP server it is connected to.
     */
    public String pwd() throws IOException 
    {
        sendLine("PWD");
        String response = readLine();
        
        return response;
    }
    
    /**
     * Returns a list of files from a directory specified.
     */
    public File ls() throws IOException 
    {
        sendLine("LIST");
        // TODO Add the necessary code to server and client to send and receive the complete file tree
   
        try {
        	/* Change : We recover the File from the server*/
        	fileTree = (File)entree.readObject();
        	System.out.println("tree file receive : " +fileTree.toString());
        	} catch (ClassNotFoundException e) {
        	// TODO Auto-generated catch block
        	e.printStackTrace();
        	}
        /* Change : We return the file*/
        	return fileTree;
    }
    

    /**
     * Changes the working directory (like cd). Returns a file if successful.
     */
    
    public File cwd (String dir) throws IOException 
    {
        sendLine("CWD " + dir);
        try {
        	/* Change : We recover the File from the server*/
        	fileTree = (File)entree.readObject();
        	System.out.println("tree file receive : " +fileTree.toString());
        	} catch (ClassNotFoundException e) {
        	// TODO Auto-generated catch block
        	e.printStackTrace();
        	}
        /* Change : We return the file*/
        	return fileTree;
    
    }
    
    
    public boolean stor(File file) throws IOException 
    {
        if (file.isDirectory()) {
            throw new IOException("SimpleFTP cannot upload a directory.");
        }        
        String filename = file.getName();
        return stor (new FileInputStream(file), filename);
    }
    
    
    /**
     * Sends a file to be stored on the FTP server.
     * Returns true if the file transfer was successful.
     * The file is sent in passive mode to avoid NAT or firewall problems
     * at the client end.
     */
    public  boolean stor(InputStream inputStream, String filename) throws IOException {

        BufferedInputStream input = new BufferedInputStream(inputStream);
        
        sendLine("PASV");
        String response = readLine();
        if (!response.startsWith("227 ")) {
            throw new IOException("SimpleFTP could not request passive mode: " + response);
        }
        
        String ip = null;
        int port = -1;
        int opening = response.indexOf('(');
        int closing = response.indexOf(')', opening + 1);
        if (closing > 0) {
            String dataLink = response.substring(opening + 1, closing);
            StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
            try {
                ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken();
                port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());
            }
            catch (Exception e) {
                throw new IOException("SimpleFTP received bad data link information: " + response);
            }
        }
        
        sendLine("STOR " + filename);
        
        Socket dataSocket = new Socket(ip, port);
        
        response = readLine();
        if (!response.startsWith("150 ")) {
            throw new IOException("SimpleFTP was not allowed to send the file: " + response);
        }
        sendLine(getUpload());
        BufferedOutputStream output = new BufferedOutputStream(dataSocket.getOutputStream());
        byte[] buffer = new byte[4096];
        int bytesRead = 0;
        while ((bytesRead = input.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        output.flush();
        output.close();
        input.close();
        
        response = readLine();
        return response.startsWith("226 ");
    }

    /* Change : Download function*/
    public boolean download(String path, File dest) throws IOException 
    {   
    	 sendLine("PASV");
         String response = readLine();
         if (!response.startsWith("227 "))
             throw new IOException("SimpleFTP could not request passive mode: " + response);
         
         String ip = null;
         int port = -1;
         int opening = response.indexOf('(');
         int closing = response.indexOf(')', opening + 1);
         if (closing > 0) {
             String dataLink = response.substring(opening + 1, closing);
             StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
             try {
                 ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken();
                 port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());
             } catch (Exception e) {
                 throw new IOException("SimpleFTP received bad data link information: " + response);
             }
         }
         
         sendLine("RETR " + path);

         Socket dataSocket = new Socket(ip, port);
         response = readLine();
         if (!response.startsWith("150 "))
             throw new IOException("SimpleFTP was not allowed to send the file: " + response);
         
         /* We send the new path to the server, it is really important to do this to be able to download files
          * from another directory than the initial one with System.getProperty("user.dir")
          */
         sendLine(newPath);
         BufferedInputStream inputStream = new BufferedInputStream(dataSocket.getInputStream());    
         FileOutputStream output = new FileOutputStream(new File(
                                                   dest.getAbsolutePath() 
                                                 + System.getProperty("file.separator")
                                                 + path));
                         byte[] buffer = new byte[4096];
                         int bytesRead = 0;
                         while ((bytesRead = inputStream.read(buffer)) != -1)
                        	 
                                 output.write(buffer, 0, bytesRead);
         
                 output.flush();
                 output.close();
         inputStream.close();
         
         response = readLine();
         return response.startsWith("226 ");
    }

    /**
     * Enter binary mode for sending binary files.
     */
    public boolean bin() throws IOException {
        sendLine("TYPE I");
        String response = readLine();
        return (response.startsWith("201 "));
    }
    
    
    /**
     * Enter ASCII mode for sending text files. This is usually the default
     * mode. Make sure you use binary mode if you are sending images or
     * other binary data, as ASCII mode is likely to corrupt them.
     */
    public boolean ascii() throws IOException {
        sendLine("TYPE A");
        String response = readLine();
        return (response.startsWith("202 "));
    }
    
    
    /**
     * Sends a raw command to the FTP server.
     */
    private void sendLine(String line) throws IOException {
        if (socket == null) {
            throw new IOException("SimpleFTP is not connected.");
        }
        try {
            writer.write(line + "\r\n");
            writer.flush();
            if (DEBUG) {
                System.out.println("> " + line);
            }
        }
        catch (IOException e) {
            socket = null;
            throw e;
        }
    }
    
    private String readLine() throws IOException {
        String line = reader.readLine();
        if (DEBUG) {
            System.out.println("< " + line);
        }
        return line;
    }
    /* Change : This method were added to get or set the tree or pathnames*/

    public File getFileTree() {
    	return this.fileTree;
    }
    
    public String getNewPath(){
    	return newPath;
    }
    
    public void setNewPath(String newPath){
    	this.newPath = newPath;
    }
    
    public String getUpload(){
    	return upload;
    }
    
    public void setUpload(String upload){
    	this.upload = upload;
    }
    
    private Socket socket = null;
    private BufferedReader reader = null;
    private BufferedWriter writer = null;
    // TO BE COMPLETED - 1 LINE
    /* Change : Declare a new ObjectInputStream to receive object from the server*/
    private ObjectInputStream entree = null;
    private String newPath = null;
    private File fileTree;
    private String upload = null;
    
    private static boolean DEBUG = false;
    

}